<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"><title>SpringBoot集成SpringSecurity实现前后端分离权限校验(新版配置方式)(待更新...) | 飞の博客</title><meta name="author" content="Fei"><meta name="copyright" content="Fei"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="ffffff"><meta name="description" content="SpringBoot集成SpringSecurity实现前后端分离权限校验(新版配置方式)">
<meta property="og:type" content="article">
<meta property="og:title" content="SpringBoot集成SpringSecurity实现前后端分离权限校验(新版配置方式)(待更新...)">
<meta property="og:url" content="https://tengfei2233.gitee.io/2023/6/15/SpringBoot%E9%9B%86%E6%88%90SpringSecurity%E5%AE%9E%E7%8E%B0%E5%89%8D%E5%90%8E%E7%AB%AF%E5%88%86%E7%A6%BB%E6%9D%83%E9%99%90%E6%A0%A1%E9%AA%8C-%E6%96%B0%E7%89%88%E9%85%8D%E7%BD%AE%E6%96%B9%E5%BC%8F/index.html">
<meta property="og:site_name" content="飞の博客">
<meta property="og:description" content="SpringBoot集成SpringSecurity实现前后端分离权限校验(新版配置方式)">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-6-15/security.jpg">
<meta property="article:published_time" content="2023-06-15T11:18:07.000Z">
<meta property="article:modified_time" content="2023-07-17T12:00:00.000Z">
<meta property="article:author" content="Fei">
<meta property="article:tag" content="Java">
<meta property="article:tag" content="SpringBoot">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-6-15/security.jpg"><link rel="shortcut icon" href="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/common/icon.png"><link rel="canonical" href="https://tengfei2233.gitee.io/2023/6/15/SpringBoot%E9%9B%86%E6%88%90SpringSecurity%E5%AE%9E%E7%8E%B0%E5%89%8D%E5%90%8E%E7%AB%AF%E5%88%86%E7%A6%BB%E6%9D%83%E9%99%90%E6%A0%A1%E9%AA%8C-%E6%96%B0%E7%89%88%E9%85%8D%E7%BD%AE%E6%96%B9%E5%BC%8F/index.html"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//hm.baidu.com"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><meta name="msvalidate.01" content="2ADFA30867CBC6E090E3C5B244DC311D"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.staticfile.org/font-awesome/6.3.0/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.staticfile.org/fancyapps-ui/4.0.31/fancybox.min.css" media="print" onload="this.media='all'"><script>var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?07a1f58fb95d3d4a97c2ae0a181c9964";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: {"path":"/search.xml","preload":false,"languages":{"hits_empty":"找不到您查询的内容：${query}"}},
  translate: {"defaultEncoding":2,"translateDelay":0,"msgToTraditionalChinese":"繁","msgToSimplifiedChinese":"簡"},
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":200},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.staticfile.org/flickr-justified-gallery/2.1.2/fjGallery.min.js',
      css: 'https://cdn.staticfile.org/flickr-justified-gallery/2.1.2/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: true,
  islazyload: false,
  isAnchor: false,
  percent: {
    toc: true,
    rightside: true,
  }
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'SpringBoot集成SpringSecurity实现前后端分离权限校验(新版配置方式)(待更新...)',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2023-07-17 20:00:00'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
    win.getCSS = (url,id = false) => new Promise((resolve, reject) => {
      const link = document.createElement('link')
      link.rel = 'stylesheet'
      link.href = url
      if (id) link.id = id
      link.onerror = reject
      link.onload = link.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        link.onload = link.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(link)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', 'ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><meta name="generator" content="Hexo 6.3.0"></head><body><div id="loading-box"><div class="loading-left-bg"></div><div class="loading-right-bg"></div><div class="spinner-box"><div class="configure-border-1"><div class="configure-core"></div></div><div class="configure-border-2"><div class="configure-core"></div></div><div class="loading-word">加载中...</div></div></div><script>const preloader = {
  endLoading: () => {
    document.body.style.overflow = 'auto';
    document.getElementById('loading-box').classList.add("loaded")
  },
  initLoading: () => {
    document.body.style.overflow = '';
    document.getElementById('loading-box').classList.remove("loaded")

  }
}
window.addEventListener('load',()=> { preloader.endLoading() })

if (false) {
  document.addEventListener('pjax:send', () => { preloader.initLoading() })
  document.addEventListener('pjax:complete', () => { preloader.endLoading() })
}</script><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/common/fei.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">24</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">28</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">16</div></a></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-link"></i><span> 友情链接</span></a></div><div class="menus_item"><a class="site-page" target="_blank" rel="noopener" href="https://github.com/tengfei2233"><i class="fa-fw fas fa-heart"></i><span> 关于我</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-6-15/security.jpg')"><nav id="nav"><span id="blog-info"><a href="/" title="飞の博客"><span class="site-name">飞の博客</span></a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search" href="javascript:void(0);"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-link"></i><span> 友情链接</span></a></div><div class="menus_item"><a class="site-page" target="_blank" rel="noopener" href="https://github.com/tengfei2233"><i class="fa-fw fas fa-heart"></i><span> 关于我</span></a></div></div><div id="toggle-menu"><a class="site-page" href="javascript:void(0);"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">SpringBoot集成SpringSecurity实现前后端分离权限校验(新版配置方式)(待更新...)</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2023-06-15T11:18:07.000Z" title="发表于 2023-06-15 19:18:07">2023-06-15</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-07-17T12:00:00.000Z" title="更新于 2023-07-17 20:00:00">2023-07-17</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/SpringBoot/">SpringBoot</a><i class="fas fa-angle-right post-meta-separator"></i><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/SpringBoot/SpringSecurity/">SpringSecurity</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">2.7k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>12分钟</span></span><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="SpringBoot集成SpringSecurity实现前后端分离权限校验(新版配置方式)(待更新...)"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h3 id="一、介绍"><a href="#一、介绍" class="headerlink" title="一、介绍"></a>一、介绍</h3><blockquote>
<p>引用<a target="_blank" rel="noopener" href="https://spring.io/projects/spring-security">SpringSecurity</a>官网的一句话：</p>
<p>Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.</p>
<p>Spring 安全性是一个功能强大且高度可定制的身份验证和访问控制框架。它是保护基于 Spring 的应用程序的事实标准。</p>
<p>Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements</p>
<p>Spring 安全性是一个专注于为 Java 应用程序提供身份验证和授权的框架。像所有 Spring 项目一样，Spring 安全性的真正力量在于它可以轻松扩展以满足自定义需求。</p>
</blockquote>
<blockquote>
<p>SpringSecurity是Spring体系下的一个安全框架，常用于用户认证与角色授权相关；因为出自于Spirng家族，所以与Spring高度融合。使用的话个人感觉较Shiro更难一些，但是如果了解其中一个的话，对于学习另一个来说也是非常快的。</p>
<p><font color=Red>其中的原理的话这里就不详细说了，直接以SpringBoot集成SpringSecurity实现前后端分离下的用户认证与授权，这里是用SpringSecurity最新的配置，相较于以前的版本有一些变化，不过大差不差。。。</font></p>
</blockquote>
<h3 id="二、引入依赖"><a href="#二、引入依赖" class="headerlink" title="二、引入依赖"></a>二、引入依赖</h3><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="comment">&lt;!-- springboot-版本 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.6.9<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 依赖坐标 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- springboot-web --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-configuration-processor<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">optional</span>&gt;</span>true<span class="tag">&lt;/<span class="name">optional</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- spring-security 5.6.6 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-security<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h3 id="三、代码编写"><a href="#三、代码编写" class="headerlink" title="三、代码编写"></a>三、代码编写</h3><h5 id="AuthenticationEntryPointImpl（认证异常处理）"><a href="#AuthenticationEntryPointImpl（认证异常处理）" class="headerlink" title="AuthenticationEntryPointImpl（认证异常处理）"></a>AuthenticationEntryPointImpl（认证异常处理）</h5><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AuthenticationEntryPointImpl</span> <span class="keyword">implements</span> <span class="title class_">AuthenticationEntryPoint</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">commence</span><span class="params">(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)</span> <span class="keyword">throws</span> IOException, ServletException &#123;</span><br><span class="line">        <span class="comment">// 这里ServletUtil.renderString就是想前端返回JSON数据</span></span><br><span class="line">        ServletUtil.renderString(response, JSON.toJSONString(R.fail(<span class="string">&quot;访问异常，请登录&quot;</span>, <span class="number">401</span>)));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// TODO ServletUtil.renderString()</span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 将字符串渲染到客户端</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> response 渲染对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> string   待渲染的字符串</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">renderString</span><span class="params">(HttpServletResponse response, String string)</span> &#123;</span><br><span class="line">    <span class="type">PrintWriter</span> <span class="variable">writer</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        response.setStatus(<span class="number">200</span>);</span><br><span class="line">        response.setContentType(<span class="string">&quot;application/json&quot;</span>);</span><br><span class="line">        response.setCharacterEncoding(<span class="string">&quot;utf-8&quot;</span>);</span><br><span class="line">        writer = response.getWriter();</span><br><span class="line">        writer.print(string);</span><br><span class="line"></span><br><span class="line">    &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        writer.flush();</span><br><span class="line">        writer.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="AccessDeniedHandlerImpl（授权异常处理）"><a href="#AccessDeniedHandlerImpl（授权异常处理）" class="headerlink" title="AccessDeniedHandlerImpl（授权异常处理）"></a>AccessDeniedHandlerImpl（授权异常处理）</h5><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AccessDeniedHandlerImpl</span> <span class="keyword">implements</span> <span class="title class_">AccessDeniedHandler</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handle</span><span class="params">(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)</span> <span class="keyword">throws</span> IOException, ServletException &#123;</span><br><span class="line">        ServletUtil.renderString(response, JSON.toJSONString(R.fail(<span class="string">&quot;访问异常，无访问权限&quot;</span>, <span class="number">403</span>)));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="LoginUser（用于存储用户登录信息）"><a href="#LoginUser（用于存储用户登录信息）" class="headerlink" title="LoginUser（用于存储用户登录信息）"></a>LoginUser（用于存储用户登录信息）</h5><p>这个类的话实现SpringSecurity的UserDetails接口，SpringSecurity上下文访问用户登录信息依靠此类，<font color=Red>此类高度自定义，可以根据自己需要的功能自定义（）</font></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// TODO 不可加lombok的@Data注解</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LoginUser</span> <span class="keyword">implements</span> <span class="title class_">UserDetails</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// TODO 我这个例子只是演示，没有将用户，角色，权限分开，正常使用的话这里可以定义用户的登录信息，用户的权限集合等。</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Long userId;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> User user;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 登录时间</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> Long loginTime;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 过期时间</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> Long expireTime;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">LoginUser</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.userId = user.getUserId();</span><br><span class="line">        <span class="built_in">this</span>.user = user;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 权限集合</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Collection&lt;? <span class="keyword">extends</span> <span class="title class_">GrantedAuthority</span>&gt; getAuthorities() &#123;</span><br><span class="line">        <span class="comment">// TODO 此处我将用户的权限放在了user信息中，其实可以通过构造器，分开传入权限列表</span></span><br><span class="line">        <span class="keyword">return</span> Arrays.asList(<span class="keyword">new</span> <span class="title class_">SimpleGrantedAuthority</span>( user.getUserType().toString()));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getPassword</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> user.getPassword();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getUsername</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> user.getUsername();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 账户是否未过期</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@JSONField(serialize = false)</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isAccountNonExpired</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 账户是否为解锁，锁定的账户无法验证</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@JSONField(serialize = false)</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isAccountNonLocked</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@JSONField(serialize = false)</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isCredentialsNonExpired</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 是否可用</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@JSONField(serialize = false)</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEnabled</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Long <span class="title function_">getUserId</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> userId;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setUserId</span><span class="params">(Long userId)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.userId = userId;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">getUser</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> user;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setUser</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.user = user;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Long <span class="title function_">getLoginTime</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> loginTime;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setLoginTime</span><span class="params">(Long loginTime)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.loginTime = loginTime;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Long <span class="title function_">getExpireTime</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> expireTime;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setExpireTime</span><span class="params">(Long expireTime)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.expireTime = expireTime;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="AuthenticationContextHolder（定义存储Authentication的ThreadLocal）"><a href="#AuthenticationContextHolder（定义存储Authentication的ThreadLocal）" class="headerlink" title="AuthenticationContextHolder（定义存储Authentication的ThreadLocal）"></a>AuthenticationContextHolder（定义存储Authentication的ThreadLocal）</h5><p>为什么要使用ThreadLocal，因为以下UserDetailsServiceImpl中的loadUserByUsername我们不能主动调用，要通过SpringSecurity来调用，其中的入参username就是从Authentication中获取。对于ThreadLocal来说是一个容器，在多线程下保存每一个线程的副本，因为对于登录来说，如果同一时间多个人登录，可能照成获取到username产生混乱，ThreadLocal的多线程安全则避开了这一情况。如果说有更好的方法，也可自定义。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AuthenticationContextHolder</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> ThreadLocal&lt;Authentication&gt; contextHolder = <span class="keyword">new</span> <span class="title class_">ThreadLocal</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Authentication <span class="title function_">getContext</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> contextHolder.get();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">setContext</span><span class="params">(Authentication context)</span> &#123;</span><br><span class="line">        contextHolder.set(context);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">clearContext</span><span class="params">()</span> &#123;</span><br><span class="line">        contextHolder.remove();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="UserDetailsServiceImpl（此类用于查询用户的信息，登录时供SpringSecurity调用）"><a href="#UserDetailsServiceImpl（此类用于查询用户的信息，登录时供SpringSecurity调用）" class="headerlink" title="UserDetailsServiceImpl（此类用于查询用户的信息，登录时供SpringSecurity调用）"></a>UserDetailsServiceImpl（此类用于查询用户的信息，登录时供SpringSecurity调用）</h5><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserDetailsServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">UserDetailsService</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Resource</span></span><br><span class="line">    <span class="keyword">private</span> UserMapper userMapper;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> UserDetails <span class="title function_">loadUserByUsername</span><span class="params">(String username)</span> <span class="keyword">throws</span> UsernameNotFoundException &#123;</span><br><span class="line">        <span class="comment">// 一般在此处定义</span></span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> userMapper.selectOne(<span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;User&gt;()</span><br><span class="line">                                         .eq(User::getUsername, username));</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (ObjUtil.isNull(user)) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UserException</span>(<span class="string">&quot;账号不存在&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (user.getStatus() == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UserException</span>(<span class="string">&quot;账号被锁定&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 比较密码</span></span><br><span class="line">        <span class="type">Authentication</span> <span class="variable">authentication</span> <span class="operator">=</span> AuthenticationContextHolder.getContext();</span><br><span class="line">        <span class="type">String</span> <span class="variable">password</span> <span class="operator">=</span> authentication.getCredentials().toString();</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">pwdPass</span> <span class="operator">=</span> SecurityUtil.matchesPassword(password, user.getPassword());</span><br><span class="line">        <span class="keyword">if</span> (!pwdPass) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UserException</span>(<span class="string">&quot;密码错误&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        authentication.remove();</span><br><span class="line">        <span class="comment">// TODO 不能设置为空</span></span><br><span class="line">        <span class="comment">// user.setPassword(null);</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">LoginUser</span>(user);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 当中提到的：SecurityUtil.matchesPassword()，判断加密密码与原密码进行对比</span></span><br></pre></td></tr></table></figure>

<h5 id="AesPasswordEncoder与SecurityUtil，密码加密类与安全工具类"><a href="#AesPasswordEncoder与SecurityUtil，密码加密类与安全工具类" class="headerlink" title="AesPasswordEncoder与SecurityUtil，密码加密类与安全工具类"></a>AesPasswordEncoder与SecurityUtil，密码加密类与安全工具类</h5><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// TODO 加密工具类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AesPasswordEncoder</span> <span class="keyword">implements</span> <span class="title class_">PasswordEncoder</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// TODO 不要改变这里，加密的salt</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">KEY</span> <span class="operator">=</span> <span class="string">&quot;wangqingzezzzzzz&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 加密</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> rawPassword 待加密密码</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">encode</span><span class="params">(CharSequence rawPassword)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">byte</span>[] raw = KEY.getBytes(<span class="string">&quot;utf-8&quot;</span>);</span><br><span class="line">            <span class="type">SecretKeySpec</span> <span class="variable">skeySpec</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SecretKeySpec</span>(raw, <span class="string">&quot;AES&quot;</span>);</span><br><span class="line">            <span class="comment">//&quot;算法/模式/补码方式&quot;</span></span><br><span class="line">            <span class="type">Cipher</span> <span class="variable">cipher</span> <span class="operator">=</span> Cipher.getInstance(<span class="string">&quot;AES/ECB/PKCS5Padding&quot;</span>);</span><br><span class="line">            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);</span><br><span class="line">            <span class="type">byte</span>[] encrypted = cipher.doFinal(rawPassword.toString().getBytes(<span class="string">&quot;utf-8&quot;</span>));</span><br><span class="line">            <span class="comment">// base64转码</span></span><br><span class="line">            <span class="keyword">return</span> Base64.getEncoder().encodeToString(encrypted);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 比对</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> rawPassword     未加密密码</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> encodedPassword 数据库加密的密码</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">matches</span><span class="params">(CharSequence rawPassword, String encodedPassword)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> rawPassword.toString().equals(decode(encodedPassword));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 解密</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> rawPassword 待解密密码</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">decode</span><span class="params">(String rawPassword)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">byte</span>[] raw = KEY.getBytes(<span class="string">&quot;utf-8&quot;</span>);</span><br><span class="line">            <span class="type">SecretKeySpec</span> <span class="variable">skeySpec</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SecretKeySpec</span>(raw, <span class="string">&quot;AES&quot;</span>);</span><br><span class="line">            <span class="type">Cipher</span> <span class="variable">cipher</span> <span class="operator">=</span> Cipher.getInstance(<span class="string">&quot;AES/ECB/PKCS5Padding&quot;</span>);</span><br><span class="line">            cipher.init(Cipher.DECRYPT_MODE, skeySpec);</span><br><span class="line">            <span class="type">byte</span>[] decode = Base64.getDecoder().decode(rawPassword);</span><br><span class="line">            <span class="type">byte</span>[] original = cipher.doFinal(decode);</span><br><span class="line">            <span class="type">String</span> <span class="variable">originalString</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(original, <span class="string">&quot;utf-8&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span> originalString;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// TODO 安全工具类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SecurityUtil</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Long <span class="title function_">getUserId</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> getLoginUser().getUserId();</span><br><span class="line">        <span class="keyword">return</span> userId;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> LoginUser <span class="title function_">getLoginUser</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> (LoginUser) getAuthentication().getPrincipal();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UserException</span>(<span class="string">&quot;获取用户信息异常&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取Authentication</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Authentication <span class="title function_">getAuthentication</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> SecurityContextHolder.getContext().getAuthentication();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">encryptPassword</span><span class="params">(String password)</span> &#123;</span><br><span class="line">        <span class="type">AesPasswordEncoder</span> <span class="variable">passwordEncoder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AesPasswordEncoder</span>();</span><br><span class="line">        <span class="keyword">return</span> passwordEncoder.encode(password);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 判断密码是否相同</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> rawPassword     真实密码</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> encodedPassword 加密后字符</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 结果</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">boolean</span> <span class="title function_">matchesPassword</span><span class="params">(String rawPassword, String encodedPassword)</span> &#123;</span><br><span class="line">        <span class="type">AesPasswordEncoder</span> <span class="variable">passwordEncoder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AesPasswordEncoder</span>();</span><br><span class="line">        <span class="keyword">return</span> passwordEncoder.matches(rawPassword, encodedPassword);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">decodePassword</span><span class="params">(String rawPassword)</span> &#123;</span><br><span class="line">        <span class="type">AesPasswordEncoder</span> <span class="variable">passwordEncoder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AesPasswordEncoder</span>();</span><br><span class="line">        <span class="keyword">return</span> passwordEncoder.decode(rawPassword);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="JwtAuthenticationTokenFilter（登录认证过滤）"><a href="#JwtAuthenticationTokenFilter（登录认证过滤）" class="headerlink" title="JwtAuthenticationTokenFilter（登录认证过滤）"></a>JwtAuthenticationTokenFilter（登录认证过滤）</h5><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JwtAuthenticationTokenFilter</span> <span class="keyword">extends</span> <span class="title class_">OncePerRequestFilter</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// TODO 此处是Jwt工具类</span></span><br><span class="line">    <span class="meta">@Resource</span></span><br><span class="line">    <span class="keyword">private</span> JwtUtil jwtUtil;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doFilterInternal</span><span class="params">(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)</span> <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        <span class="comment">// 获取登录用户信息，以自己的方式实现</span></span><br><span class="line">        <span class="type">LoginUser</span> <span class="variable">loginUser</span> <span class="operator">=</span> jwtUtil.getLoginUser(request);</span><br><span class="line">        <span class="type">Authentication</span> <span class="variable">authentication</span> <span class="operator">=</span> SecurityContextHolder.getContext().getAuthentication();</span><br><span class="line">        <span class="keyword">if</span> (ObjUtil.isNotNull(loginUser) &amp;&amp; ObjUtil.isNull(authentication)) &#123;</span><br><span class="line">            <span class="comment">// 代表登录未授权</span></span><br><span class="line">            <span class="comment">// TODO 这里注意，这里是检查Redis中登录信息的过期时间，如果快过期了，对其进行续期处理</span></span><br><span class="line">            jwtUtil.verifyToken(loginUser);</span><br><span class="line">            <span class="comment">// 至于这里的入参，可以选择自己系统的实现方式进行调整</span></span><br><span class="line">            <span class="type">UsernamePasswordAuthenticationToken</span> <span class="variable">authenticationToken</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UsernamePasswordAuthenticationToken</span>(loginUser, <span class="literal">null</span>, loginUser.getAuthorities());</span><br><span class="line">            authenticationToken.setDetails(<span class="keyword">new</span> <span class="title class_">WebAuthenticationDetailsSource</span>().buildDetails(request));</span><br><span class="line">            SecurityContextHolder.getContext().setAuthentication(authenticationToken);</span><br><span class="line">        &#125;</span><br><span class="line">        filterChain.doFilter(request, response);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// TODO 附上jwtUtil.verifyToken()是如何进行登录信息续期的。</span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 当里过期时间还有10min时，刷新令牌</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> loginUser</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">verifyToken</span><span class="params">(LoginUser loginUser)</span> &#123;</span><br><span class="line">    <span class="type">long</span> <span class="variable">expireTime</span> <span class="operator">=</span> loginUser.getExpireTime();</span><br><span class="line">    <span class="type">long</span> <span class="variable">currentTime</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    <span class="keyword">if</span> (expireTime - currentTime &lt;= <span class="number">10</span> * MINUTE) &#123;</span><br><span class="line">        refreshToken(loginUser);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 刷新令牌有效期</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> loginUser 登录信息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">refreshToken</span><span class="params">(LoginUser loginUser)</span> &#123;</span><br><span class="line">    loginUser.setLoginTime(System.currentTimeMillis());</span><br><span class="line">    loginUser.setExpireTime(loginUser.getLoginTime() + expire * MINUTE);</span><br><span class="line">    <span class="comment">// 根据uuid将loginUser缓存</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">userKey</span> <span class="operator">=</span> RedisKey.LOGIN_USER_KEY + (loginUser.getUserId());</span><br><span class="line">    <span class="comment">// 其中expire是你的登录信息缓存过期时间</span></span><br><span class="line">    redisUtil.set(userKey, loginUser, expire);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="LogoutService（退出登录处理器，不需要我们编写退出登录的Service，使用SpringSecurity自带的）"><a href="#LogoutService（退出登录处理器，不需要我们编写退出登录的Service，使用SpringSecurity自带的）" class="headerlink" title="LogoutService（退出登录处理器，不需要我们编写退出登录的Service，使用SpringSecurity自带的）"></a>LogoutService（退出登录处理器，不需要我们编写退出登录的Service，使用SpringSecurity自带的）</h5><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// TODO 此Servic不需要我们自己调用</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LogoutService</span> <span class="keyword">implements</span> <span class="title class_">LogoutSuccessHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Resource</span></span><br><span class="line">    <span class="keyword">private</span> JwtUtil jwtUtil;</span><br><span class="line">    <span class="meta">@Resource</span></span><br><span class="line">    <span class="keyword">private</span> RedisUtil redisUtil;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onLogoutSuccess</span><span class="params">(HttpServletRequest request, HttpServletResponse response, Authentication authentication)</span> <span class="keyword">throws</span> IOException, ServletException &#123;</span><br><span class="line">        <span class="type">LoginUser</span> <span class="variable">loginUser</span> <span class="operator">=</span> jwtUtil.getLoginUser(request);</span><br><span class="line">        <span class="keyword">if</span> (ObjUtil.isNotNull(loginUser)) &#123;</span><br><span class="line">                redisUtil.del(RedisKey.LOGIN_USER_KEY + loginUser.getUserId());</span><br><span class="line">        &#125;</span><br><span class="line">        ServletUtil.renderString(response, JSON.toJSONString(R.ok(<span class="string">&quot;退出登录成功&quot;</span>)));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="SpringSecurityConfig（SpringSecurity的核心配置）"><a href="#SpringSecurityConfig（SpringSecurity的核心配置）" class="headerlink" title="SpringSecurityConfig（SpringSecurity的核心配置）"></a>SpringSecurityConfig（SpringSecurity的核心配置）</h5><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// TODO 如果需要开启注解授权，需要替换下注解，可以搜索查询下，这里有时间更新</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableWebSecurity</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringSecurityConfig</span> &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Resource</span></span><br><span class="line">    <span class="keyword">private</span> AuthenticationEntryPointImpl authenticationEntryPoint;</span><br><span class="line">    <span class="meta">@Resource</span></span><br><span class="line">    <span class="keyword">private</span> AccessDeniedHandlerImpl accessDeniedHandler;</span><br><span class="line">    <span class="meta">@Resource</span></span><br><span class="line">    <span class="keyword">private</span> JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;</span><br><span class="line">    <span class="meta">@Resource</span></span><br><span class="line">    <span class="keyword">private</span> UserDetailsService userDetailsService;</span><br><span class="line">    <span class="meta">@Resource</span></span><br><span class="line">    <span class="keyword">private</span> LogoutService logoutService;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取AuthenticationManager（认证管理器），登录时认证使用</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> authenticationConfiguration</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> AuthenticationManager <span class="title function_">authenticationManager</span><span class="params">(AuthenticationConfiguration authenticationConfiguration)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="keyword">return</span> authenticationConfiguration.getAuthenticationManager();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SecurityFilterChain <span class="title function_">filterChain</span><span class="params">(HttpSecurity http)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="keyword">return</span> http</span><br><span class="line">            <span class="comment">// 异常处理</span></span><br><span class="line">            .exceptionHandling(handle -&gt; handle</span><br><span class="line">                               <span class="comment">// 认证异常（没登陆）</span></span><br><span class="line">                               .authenticationEntryPoint(authenticationEntryPoint)</span><br><span class="line">                               <span class="comment">// 访问异常（没权限）</span></span><br><span class="line">                               .accessDeniedHandler(accessDeniedHandler)</span><br><span class="line">                              )</span><br><span class="line">            <span class="comment">// 基于token，不需要csrf</span></span><br><span class="line">            .csrf().disable()</span><br><span class="line">            <span class="comment">// 基于token，不需要session</span></span><br><span class="line">            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()</span><br><span class="line">            <span class="comment">// 设置权限</span></span><br><span class="line">            .authorizeRequests(auth -&gt; auth</span><br><span class="line">                               <span class="comment">// 请求放开</span></span><br><span class="line">                               <span class="comment">// swagger</span></span><br><span class="line">                               .antMatchers(<span class="string">&quot;/favicon.ico&quot;</span>, <span class="string">&quot;/doc.html&quot;</span>, <span class="string">&quot;/webjars/**&quot;</span>, <span class="string">&quot;/swagger-resources/**&quot;</span>, <span class="string">&quot;/v3/api-docs/**&quot;</span>).permitAll()</span><br><span class="line">                               <span class="comment">// 支付</span></span><br><span class="line">                               .antMatchers(<span class="string">&quot;/user/book/syncNotify&quot;</span>,<span class="string">&quot;/user/book/asyncNotify&quot;</span>).permitAll()</span><br><span class="line">                               <span class="comment">// 方形接口（具体匹配接口放上面，防止匹配顺序问题）</span></span><br><span class="line">                               .antMatchers(<span class="string">&quot;/user/login/**&quot;</span>, <span class="string">&quot;/manage/login/login&quot;</span>).permitAll()</span><br><span class="line">                               <span class="comment">// TODO hasRole会给权限字符串加上 ROLE_ 前缀，hasAuthority不会</span></span><br><span class="line">                               <span class="comment">// TODO 如果需要实现更多权限，可以在此基础上拓展，以后再来更新</span></span><br><span class="line">                               <span class="comment">// 普通用户接口（拥有0权限字符串的用户可以访问）</span></span><br><span class="line">                               .antMatchers(<span class="string">&quot;/user/**&quot;</span>).hasAuthority(<span class="string">&quot;0&quot;</span>)</span><br><span class="line">                               <span class="comment">// 管理员接口（拥有1权限字符串的用户可以访问）</span></span><br><span class="line">                               .antMatchers(<span class="string">&quot;/manage/**&quot;</span>).hasAuthority(<span class="string">&quot;1&quot;</span>)</span><br><span class="line">                               <span class="comment">// 其它全部需要验证</span></span><br><span class="line">                               .anyRequest().authenticated()</span><br><span class="line">                              )</span><br><span class="line">            <span class="comment">// 通配/user/logout与/manage/logout</span></span><br><span class="line">            .logout().logoutUrl(<span class="string">&quot;/*/logout&quot;</span>).logoutSuccessHandler(logoutService).and()</span><br><span class="line">            <span class="comment">// 访问前jwt认证（在UsernamePasswordAuthenticationFilter前加入jwt过滤器）</span></span><br><span class="line">            .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)</span><br><span class="line">            <span class="comment">// 身份认证</span></span><br><span class="line">            .userDetailsService(userDetailsService)</span><br><span class="line">            .build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 密码明文加密方式配置</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> PasswordEncoder <span class="title function_">passwordEncoder</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">AesPasswordEncoder</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 跨域支持</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> CorsConfigurationSource <span class="title function_">corsConfigurationSource</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">UrlBasedCorsConfigurationSource</span> <span class="variable">source</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UrlBasedCorsConfigurationSource</span>();</span><br><span class="line">        <span class="comment">// 所有请求都支持跨域</span></span><br><span class="line">        source.registerCorsConfiguration(<span class="string">&quot;/**&quot;</span>, <span class="keyword">new</span> <span class="title class_">CorsConfiguration</span>().applyPermitDefaultValues());</span><br><span class="line">        <span class="keyword">return</span> source;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="四、关于其中一些细节部分，以及相关的注解配置权限，以后再来更新。。。。。。"><a href="#四、关于其中一些细节部分，以及相关的注解配置权限，以后再来更新。。。。。。" class="headerlink" title="四、关于其中一些细节部分，以及相关的注解配置权限，以后再来更新。。。。。。"></a><font color=Red>四、关于其中一些细节部分，以及相关的注解配置权限，以后再来更新。。。。。。</font></h3></article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="https://tengfei2233.gitee.io">Fei</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://tengfei2233.gitee.io/2023/6/15/SpringBoot%E9%9B%86%E6%88%90SpringSecurity%E5%AE%9E%E7%8E%B0%E5%89%8D%E5%90%8E%E7%AB%AF%E5%88%86%E7%A6%BB%E6%9D%83%E9%99%90%E6%A0%A1%E9%AA%8C-%E6%96%B0%E7%89%88%E9%85%8D%E7%BD%AE%E6%96%B9%E5%BC%8F/">https://tengfei2233.gitee.io/2023/6/15/SpringBoot%E9%9B%86%E6%88%90SpringSecurity%E5%AE%9E%E7%8E%B0%E5%89%8D%E5%90%8E%E7%AB%AF%E5%88%86%E7%A6%BB%E6%9D%83%E9%99%90%E6%A0%A1%E9%AA%8C-%E6%96%B0%E7%89%88%E9%85%8D%E7%BD%AE%E6%96%B9%E5%BC%8F/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://tengfei2233.gitee.io" target="_blank">飞の博客</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/Java/">Java</a><a class="post-meta__tags" href="/tags/SpringBoot/">SpringBoot</a></div><div class="post_share"><div class="social-share" data-image="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-6-15/security.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.staticfile.org/butterfly-extsrc/1.1.3/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.staticfile.org/butterfly-extsrc/1.1.3/sharejs/dist/js/social-share.min.js" defer></script></div></div><div class="post-reward"><div class="reward-button"><i class="fas fa-qrcode"></i> 打赏</div><div class="reward-main"><ul class="reward-all"><li class="reward-item"><a href="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/common/wechat.png" target="_blank"><img class="post-qr-code-img" src="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/common/wechat.png" alt="微信"/></a><div class="post-qr-code-desc">微信</div></li><li class="reward-item"><a href="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/common/alipay.png" target="_blank"><img class="post-qr-code-img" src="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/common/alipay.png" alt="支付宝"/></a><div class="post-qr-code-desc">支付宝</div></li></ul></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/2023/6/15/Jackson%E8%A7%A3%E5%86%B3SpringMVC%E5%85%A8%E5%B1%80%E5%BA%8F%E5%88%97%E5%8C%96%E8%BF%94%E5%9B%9E%E4%B8%ADLong%E7%B1%BB%E5%9E%8B%E7%B2%BE%E5%BA%A6%E4%B8%A2%E5%A4%B1%E9%97%AE%E9%A2%98/" title="配置SpringMvc全局序列化与反序列化，解决Long类型下精度丢失问题（使用FastJson2）"><img class="cover" src="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-6-15/js.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">配置SpringMvc全局序列化与反序列化，解决Long类型下精度丢失问题（使用FastJson2）</div></div></a></div><div class="next-post pull-right"><a href="/2023/6/25/Java%E5%8E%9F%E7%94%9FNio%E6%9C%8D%E5%8A%A1%E7%AB%AF%E4%B8%8E%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%BC%96%E5%86%99/" title="Java原生Nio服务端与客户端编写"><img class="cover" src="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-6-25/Nio.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">Java原生Nio服务端与客户端编写</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/2023/11/21/netty%E7%9A%84%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8%EF%BC%8C%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90%EF%BC%8C%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3-%E6%9B%B4%E6%96%B0%E4%B8%AD/" title="netty的基本使用，基本原理分析，相关问题解(更新中...)"><img class="cover" src="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-11-10/netty-ai.png" alt="cover"><div class="content is-center"><div class="date"><i class="fas fa-history fa-fw"></i> 2023-11-21</div><div class="title">netty的基本使用，基本原理分析，相关问题解(更新中...)</div></div></a></div><div><a href="/2023/6/15/Jackson%E8%A7%A3%E5%86%B3SpringMVC%E5%85%A8%E5%B1%80%E5%BA%8F%E5%88%97%E5%8C%96%E8%BF%94%E5%9B%9E%E4%B8%ADLong%E7%B1%BB%E5%9E%8B%E7%B2%BE%E5%BA%A6%E4%B8%A2%E5%A4%B1%E9%97%AE%E9%A2%98/" title="配置SpringMvc全局序列化与反序列化，解决Long类型下精度丢失问题（使用FastJson2）"><img class="cover" src="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-6-15/js.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="fas fa-history fa-fw"></i> 2023-06-19</div><div class="title">配置SpringMvc全局序列化与反序列化，解决Long类型下精度丢失问题（使用FastJson2）</div></div></a></div><div><a href="/2023/2/27/SpringBoot%E9%9B%86%E6%88%90%E6%94%AF%E4%BB%98%E5%AE%9D%E6%B2%99%E7%AE%B1%E6%94%AF%E4%BB%98/" title="SpringBoot集成支付宝沙箱支付"><img class="cover" src="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-3-2/alipay.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="fas fa-history fa-fw"></i> 2023-02-27</div><div class="title">SpringBoot集成支付宝沙箱支付</div></div></a></div><div><a href="/2023/2/12/SpringBoot%E9%9B%86%E6%88%90Redisson%E6%9B%BF%E6%8D%A2SpringCache%E5%BA%95%E5%B1%82%E5%AE%B9%E5%99%A8%E5%AE%9E%E7%8E%B0/" title="SpringBoot集成Redisson替换SpringCache底层容器实现"><img class="cover" src="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-2-26/redisson.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="fas fa-history fa-fw"></i> 2023-02-12</div><div class="title">SpringBoot集成Redisson替换SpringCache底层容器实现</div></div></a></div><div><a href="/2023/2/10/SpringBoot%E9%9B%86%E6%88%90Redisson%E5%AE%9E%E7%8E%B0%E6%B3%A8%E8%A7%A3%E9%99%90%E6%B5%81/" title="SpringBoot集成Redisson实现注解限流"><img class="cover" src="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-2-26/redisson.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="fas fa-history fa-fw"></i> 2023-02-12</div><div class="title">SpringBoot集成Redisson实现注解限流</div></div></a></div><div><a href="/2023/1/10/SpringBoot%E9%9B%86%E6%88%90Netty%E5%AE%9E%E7%8E%B0Socket%E9%80%9A%E4%BF%A1/" title="SpringBoot集成Netty实现Socket通信"><img class="cover" src="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-2-24/netty.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="fas fa-history fa-fw"></i> 2023-01-10</div><div class="title">SpringBoot集成Netty实现Socket通信</div></div></a></div></div></div><hr/><div id="post-comment"><div class="comment-head"><div class="comment-headline"><i class="fas fa-comments fa-fw"></i><span> 评论</span></div></div><div class="comment-wrap"><div><div class="vcomment" id="vcomment"></div></div></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/common/fei.jpg" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">Fei</div><div class="author-info__description">当你想起来了，那就记录一点吧</div></div><div class="card-info-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">24</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">28</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">16</div></a></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/tengfei2233"><i class="fab fa-github"></i><span>关注我</span></a><div class="card-info-social-icons is-center"><a class="social-icon" href="https://github.com/tengfei2233" target="_blank" title="Github"><i class="fab fa-github"></i></a><a class="social-icon" href="https://gitee.com/tengfei2233" target="_blank" title="Gitee"><i class="fab fa-git"></i></a><a class="social-icon" href="/youfeige@outlook.com" target="_blank" title="Email"><i class="fas fa-envelope"></i></a></div></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn fa-shake"></i><span>公告</span></div><div class="announcement_content">当你迷茫的时候，那就静下心想想吧！</div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%80%E3%80%81%E4%BB%8B%E7%BB%8D"><span class="toc-number">1.</span> <span class="toc-text">一、介绍</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BA%8C%E3%80%81%E5%BC%95%E5%85%A5%E4%BE%9D%E8%B5%96"><span class="toc-number">2.</span> <span class="toc-text">二、引入依赖</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%89%E3%80%81%E4%BB%A3%E7%A0%81%E7%BC%96%E5%86%99"><span class="toc-number">3.</span> <span class="toc-text">三、代码编写</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#AuthenticationEntryPointImpl%EF%BC%88%E8%AE%A4%E8%AF%81%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%EF%BC%89"><span class="toc-number">3.0.1.</span> <span class="toc-text">AuthenticationEntryPointImpl（认证异常处理）</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#AccessDeniedHandlerImpl%EF%BC%88%E6%8E%88%E6%9D%83%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%EF%BC%89"><span class="toc-number">3.0.2.</span> <span class="toc-text">AccessDeniedHandlerImpl（授权异常处理）</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#LoginUser%EF%BC%88%E7%94%A8%E4%BA%8E%E5%AD%98%E5%82%A8%E7%94%A8%E6%88%B7%E7%99%BB%E5%BD%95%E4%BF%A1%E6%81%AF%EF%BC%89"><span class="toc-number">3.0.3.</span> <span class="toc-text">LoginUser（用于存储用户登录信息）</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#AuthenticationContextHolder%EF%BC%88%E5%AE%9A%E4%B9%89%E5%AD%98%E5%82%A8Authentication%E7%9A%84ThreadLocal%EF%BC%89"><span class="toc-number">3.0.4.</span> <span class="toc-text">AuthenticationContextHolder（定义存储Authentication的ThreadLocal）</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#UserDetailsServiceImpl%EF%BC%88%E6%AD%A4%E7%B1%BB%E7%94%A8%E4%BA%8E%E6%9F%A5%E8%AF%A2%E7%94%A8%E6%88%B7%E7%9A%84%E4%BF%A1%E6%81%AF%EF%BC%8C%E7%99%BB%E5%BD%95%E6%97%B6%E4%BE%9BSpringSecurity%E8%B0%83%E7%94%A8%EF%BC%89"><span class="toc-number">3.0.5.</span> <span class="toc-text">UserDetailsServiceImpl（此类用于查询用户的信息，登录时供SpringSecurity调用）</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#AesPasswordEncoder%E4%B8%8ESecurityUtil%EF%BC%8C%E5%AF%86%E7%A0%81%E5%8A%A0%E5%AF%86%E7%B1%BB%E4%B8%8E%E5%AE%89%E5%85%A8%E5%B7%A5%E5%85%B7%E7%B1%BB"><span class="toc-number">3.0.6.</span> <span class="toc-text">AesPasswordEncoder与SecurityUtil，密码加密类与安全工具类</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#JwtAuthenticationTokenFilter%EF%BC%88%E7%99%BB%E5%BD%95%E8%AE%A4%E8%AF%81%E8%BF%87%E6%BB%A4%EF%BC%89"><span class="toc-number">3.0.7.</span> <span class="toc-text">JwtAuthenticationTokenFilter（登录认证过滤）</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#LogoutService%EF%BC%88%E9%80%80%E5%87%BA%E7%99%BB%E5%BD%95%E5%A4%84%E7%90%86%E5%99%A8%EF%BC%8C%E4%B8%8D%E9%9C%80%E8%A6%81%E6%88%91%E4%BB%AC%E7%BC%96%E5%86%99%E9%80%80%E5%87%BA%E7%99%BB%E5%BD%95%E7%9A%84Service%EF%BC%8C%E4%BD%BF%E7%94%A8SpringSecurity%E8%87%AA%E5%B8%A6%E7%9A%84%EF%BC%89"><span class="toc-number">3.0.8.</span> <span class="toc-text">LogoutService（退出登录处理器，不需要我们编写退出登录的Service，使用SpringSecurity自带的）</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#SpringSecurityConfig%EF%BC%88SpringSecurity%E7%9A%84%E6%A0%B8%E5%BF%83%E9%85%8D%E7%BD%AE%EF%BC%89"><span class="toc-number">3.0.9.</span> <span class="toc-text">SpringSecurityConfig（SpringSecurity的核心配置）</span></a></li></ol></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%9B%9B%E3%80%81%E5%85%B3%E4%BA%8E%E5%85%B6%E4%B8%AD%E4%B8%80%E4%BA%9B%E7%BB%86%E8%8A%82%E9%83%A8%E5%88%86%EF%BC%8C%E4%BB%A5%E5%8F%8A%E7%9B%B8%E5%85%B3%E7%9A%84%E6%B3%A8%E8%A7%A3%E9%85%8D%E7%BD%AE%E6%9D%83%E9%99%90%EF%BC%8C%E4%BB%A5%E5%90%8E%E5%86%8D%E6%9D%A5%E6%9B%B4%E6%96%B0%E3%80%82%E3%80%82%E3%80%82%E3%80%82%E3%80%82%E3%80%82"><span class="toc-number">4.</span> <span class="toc-text">四、关于其中一些细节部分，以及相关的注解配置权限，以后再来更新。。。。。。</span></a></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item"><a class="thumbnail" href="/2023/11/21/netty%E7%9A%84%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8%EF%BC%8C%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90%EF%BC%8C%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3-%E6%9B%B4%E6%96%B0%E4%B8%AD/" title="netty的基本使用，基本原理分析，相关问题解(更新中...)"><img src="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-11-10/netty-ai.png" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="netty的基本使用，基本原理分析，相关问题解(更新中...)"/></a><div class="content"><a class="title" href="/2023/11/21/netty%E7%9A%84%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8%EF%BC%8C%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90%EF%BC%8C%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3-%E6%9B%B4%E6%96%B0%E4%B8%AD/" title="netty的基本使用，基本原理分析，相关问题解(更新中...)">netty的基本使用，基本原理分析，相关问题解(更新中...)</a><time datetime="2023-11-21T09:27:51.000Z" title="发表于 2023-11-21 17:27:51">2023-11-21</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2023/11/10/%E5%9C%A8conda%E7%8E%AF%E5%A2%83%E4%B8%8B%E8%BF%90%E8%A1%8Cstable-diffusion-webui-AI%E7%BB%98%E5%9B%BE/" title="在conda环境下运行stable-diffusion-webui AI绘图"><img src="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-11-10/ai.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="在conda环境下运行stable-diffusion-webui AI绘图"/></a><div class="content"><a class="title" href="/2023/11/10/%E5%9C%A8conda%E7%8E%AF%E5%A2%83%E4%B8%8B%E8%BF%90%E8%A1%8Cstable-diffusion-webui-AI%E7%BB%98%E5%9B%BE/" title="在conda环境下运行stable-diffusion-webui AI绘图">在conda环境下运行stable-diffusion-webui AI绘图</a><time datetime="2023-11-10T07:30:51.000Z" title="发表于 2023-11-10 15:30:51">2023-11-10</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2023/11/10/%E5%9C%A8Ubuntu20%E4%B8%8A%E5%AE%89%E8%A3%85CUDA-cuDNN-Miniconda%E7%8E%AF%E5%A2%83(%E5%85%B6%E5%AE%83%E7%B3%BB%E7%BB%9F%E5%90%8C%E7%90%86)/" title="在Ubuntu20上安装CUDA+cuDNN+Miniconda环境(其它系统同理)"><img src="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-11-10/ai.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="在Ubuntu20上安装CUDA+cuDNN+Miniconda环境(其它系统同理)"/></a><div class="content"><a class="title" href="/2023/11/10/%E5%9C%A8Ubuntu20%E4%B8%8A%E5%AE%89%E8%A3%85CUDA-cuDNN-Miniconda%E7%8E%AF%E5%A2%83(%E5%85%B6%E5%AE%83%E7%B3%BB%E7%BB%9F%E5%90%8C%E7%90%86)/" title="在Ubuntu20上安装CUDA+cuDNN+Miniconda环境(其它系统同理)">在Ubuntu20上安装CUDA+cuDNN+Miniconda环境(其它系统同理)</a><time datetime="2023-11-10T07:27:51.000Z" title="发表于 2023-11-10 15:27:51">2023-11-10</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2023/8/27/Docker-Compose%E5%AD%A6%E4%B9%A0/" title="Docker Compose学习"><img src="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-6-26/docker.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Docker Compose学习"/></a><div class="content"><a class="title" href="/2023/8/27/Docker-Compose%E5%AD%A6%E4%B9%A0/" title="Docker Compose学习">Docker Compose学习</a><time datetime="2023-08-27T15:34:50.000Z" title="发表于 2023-08-27 23:34:50">2023-08-27</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2023/8/27/Dockerfile%E5%AD%A6%E4%B9%A0/" title="Dockerfile学习"><img src="https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-6-26/docker.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Dockerfile学习"/></a><div class="content"><a class="title" href="/2023/8/27/Dockerfile%E5%AD%A6%E4%B9%A0/" title="Dockerfile学习">Dockerfile学习</a><time datetime="2023-08-27T15:34:37.000Z" title="发表于 2023-08-27 23:34:37">2023-08-27</time></div></div></div></div></div></div></main><footer id="footer" style="background-image: url('https://blog-1306275892.cos.ap-shanghai.myqcloud.com/hexo/blogs/2023-6-15/security.jpg')"><div id="footer-wrap"><div class="copyright">&copy;2022 - 2024 By Fei</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div><div class="footer_custom_text">欢迎进入<a href="tengfei2233.gitee.io/">飞の博客</a>!</div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="translateLink" type="button" title="简繁转换">繁</button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><a id="to_comment" href="#post-comment" title="直达评论"><i class="fas fa-comments"></i></a><button id="go-up" type="button" title="回到顶部"><span class="scroll-percent"></span><i class="fas fa-arrow-up"></i></button></div></div><div><script src="https://cdn.staticfile.org/hexo-theme-butterfly/4.7.0/js/utils.min.js"></script><script src="https://cdn.staticfile.org/hexo-theme-butterfly/4.7.0/js/main.min.js"></script><script src="https://cdn.staticfile.org/hexo-theme-butterfly/4.7.0/js/tw_cn.min.js"></script><script src="https://cdn.staticfile.org/fancyapps-ui/4.0.31/fancybox.umd.min.js"></script><script src="https://cdn.staticfile.org/instant.page/5.1.1/instantpage.min.js" type="module"></script><script>function panguFn () {
  if (typeof pangu === 'object') pangu.autoSpacingPage()
  else {
    getScript('https://cdn.staticfile.org/pangu/4.0.7/pangu.min.js')
      .then(() => {
        pangu.autoSpacingPage()
      })
  }
}

function panguInit () {
  if (false){
    GLOBAL_CONFIG_SITE.isPost && panguFn()
  } else {
    panguFn()
  }
}

document.addEventListener('DOMContentLoaded', panguInit)</script><div class="js-pjax"><script>function loadValine () {
  function initValine () {
    const valine = new Valine(Object.assign({
      el: '#vcomment',
      appId: '0kbeJfFlvhvkYfqMqwwVCx1a-gzGzoHsz',
      appKey: 'rziWUw1MTHMW7d9eeCQzu9HV',
      avatar: 'monsterid',
      serverURLs: '',
      emojiMaps: "",
      path: window.location.pathname,
      visitor: false
    }, null))
  }

  if (typeof Valine === 'function') initValine() 
  else getScript('https://cdn.staticfile.org/valine/1.5.1/Valine.min.js').then(initValine)
}

if ('Valine' === 'Valine' || !true) {
  if (true) btf.loadComment(document.getElementById('vcomment'),loadValine)
  else setTimeout(loadValine, 0)
} else {
  function loadOtherComment () {
    loadValine()
  }
}</script></div><script defer="defer" id="ribbon" src="https://cdn.staticfile.org/butterfly-extsrc/1.1.3/canvas-ribbon.min.js" size="150" alpha="0.6" zIndex="-1" mobile="false" data-click="true"></script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div id="local-search-results"></div></div></div><div id="search-mask"></div><script src="https://cdn.staticfile.org/hexo-theme-butterfly/4.7.0/js/search/local-search.min.js"></script></div></body></html>